#include "stdafx.h"

#include <sstream>
using namespace std;


INIT_SINGLETON(MultiKeyboardHandler);


MultiKeyboardHandler::MultiKeyboardHandler(void)
{
}

MultiKeyboardHandler::~MultiKeyboardHandler(void)
{
	if(m_hGMHook)
		UnhookWindowsHookEx(m_hGMHook);
	if(m_hCBTHook)
		UnhookWindowsHookEx(m_hCBTHook);

}

void MultiKeyboardHandler::CaptureDevices(HWND hwnd)
{

	RAWINPUTDEVICE Rid;

	Rid.usUsagePage = 01; 
	Rid.usUsage = 06; 
	Rid.dwFlags = RIDEV_INPUTSINK | RIDEV_NOLEGACY;
	Rid.hwndTarget = hwnd;

	if (RegisterRawInputDevices(&Rid, 1, sizeof (Rid)) == FALSE) 
	{
		GetLastErrorDescription(true);
	}
}

LRESULT MultiKeyboardHandler::HandleInput( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
	UINT dwSize;

	GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, 
		sizeof(RAWINPUTHEADER));
	LPBYTE lpb = new BYTE[dwSize];
	if (lpb == NULL) 
	{
		return 0;
	} 

	if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, 
		sizeof(RAWINPUTHEADER)) != dwSize )
		OutputDebugString (TEXT("GetRawInputData doesn't return correct size !\n")); 

	RAWINPUT* raw = (RAWINPUT*)lpb;

	wostringstream sout;
	if (raw->header.dwType == RIM_TYPEKEYBOARD) 
	{
		//notify the window focused on by this keyboard
		HWND hwndFocus = NULL;
		KeyboardMap::iterator keybd = m_keyboards.find(raw->header.hDevice);
		if (keybd == m_keyboards.end())
		{
			GUITHREADINFO gti = {sizeof(GUITHREADINFO)};
			HWND hwndFocus = NULL;
			if(GetGUIThreadInfo(NULL,&gti))
			{
				hwndFocus = gti.hwndFocus;
			}
			KeyboardInfo kbi(raw->header.hDevice,hwndFocus);
			keybd = m_keyboards.insert(m_keyboards.end(),kbi.GetMapEntry());
		}

		hwndFocus = keybd->second.window;
		CWindow win(hwndFocus );
		TCHAR str[100];
		win.GetTopLevelParent().GetWindowText(str,100);
		wstringstream sout1; 
		sout1<<"top:" <<win.GetTopLevelParent().m_hWnd<<"  --child:"<<hwndFocus<<"  --"<< str<<"\r\n";
		m_view->ReplaceSel(sout1.str().c_str() );

		//ReplyMessage(0);
		SendKeyboardMessage(keybd->second, (raw->data.keyboard));

	
		sout<<"device="<<raw->header.hDevice
			<<" ,Kbd: make"<<raw->data.keyboard.MakeCode
			//<<" ,Flags:"<<raw->data.keyboard.Flags
		//	<<" ,Reserved:"<<raw->data.keyboard.Reserved 
			<<", ExtraInformation:"<<raw->data.keyboard.ExtraInformation
			<<", msg="<<raw->data.keyboard.Message
			<<", VK="<<raw->data.keyboard.VKey<<"\r\n\r\n"<<endl;
		m_view->ReplaceSel(sout.str().c_str());
	}
	else if (raw->header.dwType == RIM_TYPEMOUSE) 
	{
		sout<<"Mouse: usFlags"	<<raw->data.mouse.usFlags
			<<" ,ulButtons="	<<raw->data.mouse.ulButtons
			<<" ,usButtonFlags="	<<raw->data.mouse.usButtonFlags
			<<" ,usButtonData="	<<raw->data.mouse.usButtonData 
			<<" ,ulRawButtons="	<<raw->data.mouse.ulRawButtons 
			<<" ,lLastX="<<raw->data.mouse.lLastX
			<<" ,lLastY="<<raw->data.mouse.lLastY
			<<" ,ulExtraInformation="	<<raw->data.mouse.ulExtraInformation;

		m_view->ReplaceSel(sout.str().c_str());
	} 
   


	delete[] lpb; 
	return 0;
}

bool MultiKeyboardHandler::HookWindows(HINSTANCE hinstance)
{
	HMODULE hmod = LoadLibrary(TEXT("FocusHook.dll"));
	m_hGMHook = SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,hmod,NULL);
	//m_hCBTHook = SetWindowsHookEx(WH_CBT,CBTProc,hmod,NULL);
	
	//m_hHook = SetWindowsHookEx(WH_GETMESSAGE,KeyboardProc,hmod,NULL);
	//m_hHook = SetWindowsHookEx(WH_KEYBOARD_LL,&MultiKeyboardHandler::LowLevelKeyboardProc,hinstance,NULL);

	if(m_hGMHook == NULL)
		MessageBox(0,L"Hook failed",0,0);
	return (m_hGMHook != NULL);
}

bool bAltPressed = false;
LRESULT CALLBACK MultiKeyboardHandler::LowLevelKeyboardProc( int nCode,WPARAM wParam,LPARAM lParam )
{
	if(nCode < 0)
		return CallNextHookEx(0,nCode,wParam,lParam);

	KBDLLHOOKSTRUCT* kbd= (KBDLLHOOKSTRUCT*)lParam;
	if(kbd->vkCode == VK_TAB && (kbd->flags & LLKHF_ALTDOWN))
		return 1;
	else
		return CallNextHookEx(0,nCode,wParam,lParam);
}

void MultiKeyboardHandler::ActivateWindow( HWND hwnd )
{
	SendMessage(hwnd,WM_ACTIVATE,
		MAKEWPARAM(WA_ACTIVE,0),(LPARAM) hwnd);

}

int PreviousKeyState = 0;

void MultiKeyboardHandler::SendKeyboardMessage(KeyboardInfo& keyinfo,RAWKEYBOARD& keydata)
{
	if(keyinfo.window == NULL)
		return;
	unsigned char highbyte = 0;
	if(keydata.Message != WM_KEYDOWN)
		highbyte = highbyte | (0x80);		//set transition bit 31 ..always set on WM_KEYDOWN

	LPARAM extra = MAKELPARAM(0,MAKEWORD(keydata.MakeCode,highbyte));
	if(::PostMessage(keyinfo.window, WM_APP + keydata.Message,keydata.VKey,extra)
		>0)//keydata.ExtraInformation);
		m_view->ReplaceSel(L"sent \r\n");

	if(keydata.Message == WM_KEYDOWN || keydata.Message == WM_SYSKEYDOWN)
		PreviousKeyState = keydata.VKey;
}

/*
lParam
Specifies the repeat count, scan code, extended-key flag, context code, previous key-state flag, and transition-state flag, as shown in the following table. 
0-15
Specifies the repeat count for the current message. The value is the number of times the keystroke is autorepeated as a result of the user holding down the key. If the keystroke is held long enough, multiple messages are sent. However, the repeat count is not cumulative.
16-23
Specifies the scan code. The value depends on the OEM.
24
Specifies whether the key is an extended key, such as the right-hand ALT and CTRL keys that appear on an enhanced 101- or 102-key keyboard. The value is 1 if it is an extended key; otherwise, it is 0.
25-28
Reserved; do not use.
29
Specifies the context code. The value is always 0 for a WM_KEYDOWN message.
30
Specifies the previous key state. The value is 1 if the key is down before the message is sent, or it is zero if the key is up.
31
Specifies the transition state. The value is always zero for a WM_KEYDOWN message.
0000
*/